package top.whysu.edevice.dtu.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import top.whysu.edevice.dtu.ByteArrayDecoder;
import top.whysu.edevice.dtu.ByteArrayEncoder;
import top.whysu.edevice.dtu.CharacterConvert;
import top.whysu.edevice.dtu.ProtoInstant;

import javax.annotation.PostConstruct;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * netty客户端连接类
 *
 * @author 程就人生
 * @link https://www.jianshu.com/p/6d0e72737ab6
 * @date 2020年8月6日
 * @Description
 */
@Component
public class NettyClient {

    private static Logger log = LoggerFactory.getLogger(NettyClient.class);

    // 服务器ip地址
    private String host = "127.0.0.1";

    // 服务器端口(详见dtu-server的配置)
    private int port = 8099;

    private Channel channel;


    @Autowired
    private LoginResponseHandler loginResponseHandler;

    @Autowired
    private ExceptionClientHandler exceptionClientHandler;

    private Bootstrap bootstrap;

    private EventLoopGroup eventLoopGroup = new NioEventLoopGroup();

    @PostConstruct
    public void start() throws Exception {
        //启动客户端
        //doConnect();
    }

    /**
     * 连接操作
     */
    private void doConnect() {
        try {
            bootstrap = new Bootstrap();

            bootstrap.group(eventLoopGroup);
            bootstrap.channel(NioSocketChannel.class);
            bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
            bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
            bootstrap.remoteAddress(host, port);

            // 设置通道初始化
            bootstrap.handler(new ChannelInitializer<SocketChannel>() {
                                  public void initChannel(SocketChannel ch) {
                                      //编解码处理
                                      ch.pipeline().addLast("decoder", new ByteArrayDecoder());
                                      ch.pipeline().addLast("encoder", new ByteArrayEncoder());
                                      //登录返回处理
                                      ch.pipeline().addLast("loginHandler", loginResponseHandler);
                                      //异常处理
                                      ch.pipeline().addLast("exception", exceptionClientHandler);
                                  }
                              }
            );
            log.info("客户端开始连接");
            ChannelFuture f = bootstrap.connect();
            f.addListener(connectedListener);
        } catch (Exception e) {
            e.printStackTrace();
            log.info("客户端连接失败!" + e.getMessage());
        }
    }

    //连接关闭监听
    GenericFutureListener<ChannelFuture> closeListener = (ChannelFuture f) -> {
        log.info(new Date() + ": 连接已经断开……");
        channel = f.channel();
    };

    //连接监听
    GenericFutureListener<ChannelFuture> connectedListener = (ChannelFuture f) -> {
        final EventLoop eventLoop = f.channel().eventLoop();
        if (!f.isSuccess()) {
            log.info("连接失败!在10s之后准备尝试重连!");
            eventLoop.schedule(() -> doConnect(), 10, TimeUnit.SECONDS);
        } else {
            log.info("服务器 连接成功!" + f.channel().remoteAddress() + ":" + f.channel().localAddress());
            channel = f.channel();
            login();
        }
    };

    /**
     * 登录操作
     */
    private void login() {
        //构建登录请求
        ByteBuf buf = Unpooled.buffer(3);
        //登录
        buf.writeByte(ProtoInstant.LOGIN);
        buf.writeByte(ProtoInstant.DEVICE_ID);
        //校验位
        int sum = CharacterConvert.sum(ProtoInstant.FIELD_HEAD, 6, ProtoInstant.LOGIN, ProtoInstant.DEVICE_ID);
        int verify = CharacterConvert.getLow8(sum);
        buf.writeByte(verify);
        writeAndFlush(buf.array());
    }

    /**
     * 发送消息
     *
     * @param msg
     */
    public void writeAndFlush(Object msg) {
        this.channel.writeAndFlush(msg).addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future)
                    throws Exception {
                // 回调
                if (future.isSuccess()) {
                    log.info("请求netty服务器，消息发送成功！");
                } else {
                    log.info("请求netty服务器，消息发送失败！");
                }
            }
        });
    }

    /**
     * 重新建立连接
     *
     * @throws Exception
     */
    public void reconnect() throws Exception {
        if (channel != null && channel.isActive()) {
            return;
        }
        log.info("reconnect....");
        start();
        log.info("reconnect success");
    }

    /**
     * 关闭连接
     */
    public void close() {
        eventLoopGroup.shutdownGracefully();
    }
}
